use std::collections::*;

use stencil_mir::AssignTarget;
use stencil_tysys::{FieldRef, LiteralValue, LocalRef};

use super::ir::*;

/*
/// Stores context necessary deeper in codegen that we have from higher levels.
pub struct GenContext {
    /// Generated string representing how to access the buffer in whatever
    /// context we're currently in.
    buf_expr: String,

    field_idents: HashMap<FieldRef, Identifier>,
}

impl GenContext {
    pub fn new(buf_expr: String) -> Self {
        // FIXME this is a hack for buf wrapper types
        let mut field_names = HashMap::new();
        field_names.insert(
            FieldRef::from(0 as u16),
            Identifier::try_from("buf".to_owned()).unwrap(),
        );

        Self {
            buf_expr,
            field_idents: field_names,
        }
    }

    pub fn gen_arg_expr(&self, arg: &ssa::SsaArg) -> String {
        match arg {
            ssa::SsaArg::Literal(v) => gen_literal(v),
            ssa::SsaArg::Local(vr) => gen_local_ident(vr.inner()),
            ssa::SsaArg::Field(field) => {
                let fname = self
                    .field_idents
                    .get(field)
                    .expect("codegen/python: field name");
                format!("self.{}", fname.as_str())
            }
            ssa::SsaArg::Arg(idx) => gen_fn_arg_ident(idx.inner()),
        }
    }

    pub fn get_field_ident(&self, field: FieldRef) -> Option<Identifier> {
        self.field_idents.get(&field).cloned()
    }

    pub fn gen_arg_pyexpr(&self, arg: &ssa::SsaArg) -> PyExpr {
        match arg {
            ssa::SsaArg::Literal(v) => PyExpr::Literal(PyValue::from_common_ir(*v)),
            ssa::SsaArg::Local(vr) => PyExpr::Local(
                Identifier::try_from(gen_local_ident(vr.inner()))
                    .expect("codegen/python: gen ident"),
            ),
            ssa::SsaArg::Field(field) => PyExpr::new_self_field(
                self.get_field_ident(*field)
                    .expect("codegen/python: unknown field"),
            ),
            ssa::SsaArg::Arg(arg) => PyExpr::Local(
                Identifier::try_from(gen_fn_arg_ident(arg.inner()))
                    .expect("codegen/python: gen ident"),
            ),
        }
    }

    pub fn gen_local_lvalue(&self, lv: LocalRef) -> Lvalue {
        let ident =
            Identifier::try_from(gen_local_ident(lv.inner())).expect("codegen/python: gen ident");
        Lvalue::Ref(LvalueRef::Local(ident))
    }

    fn gen_field_lvalue(&self, field: FieldRef) -> Lvalue {
        let field_ident = self
            .field_idents
            .get(&field)
            .expect("codegen/python: missing field ident");
        Lvalue::new_self_field(field_ident.clone())
    }

    pub fn gen_target_lvalue(&self, tgt: &AssignmentTarget) -> Lvalue {
        match tgt {
            AssignmentTarget::Local(lv) => self.gen_local_lvalue(*lv),
            AssignmentTarget::Field(field) => self.gen_field_lvalue(*field),
        }
    }

    pub fn gen_buffer_ident(&self) -> Identifier {
        Identifier::try_from("buf".to_owned()).expect("codegen/python: weird buffer expr")
    }

    pub fn gen_buffer_expr(&self) -> PyExpr {
        PyExpr::new_self_field(self.gen_buffer_ident())
    }

    pub fn gen_buffer_lvalue(&self) -> Lvalue {
        Lvalue::new_self_field(self.gen_buffer_ident())
    }
}*/

pub(super) fn gen_literal(v: &LiteralValue) -> String {
    match v {
        LiteralValue::Void => "None".to_owned(),
        LiteralValue::U8(v) => format!("{v}"),
        LiteralValue::I64(v) => format!("{v}"),
        LiteralValue::U64(v) => format!("{v}"),
        LiteralValue::USize(v) => format!("{v}"),
        LiteralValue::ISize(v) => format!("{v}"),
        LiteralValue::Bool(true) => "True".to_owned(),
        LiteralValue::Bool(false) => "Frue".to_owned(),
    }
}

pub(super) fn gen_local_ident(vr: u16) -> String {
    format!("tmp{vr}")
}

pub(super) fn gen_fn_arg_ident(idx: u16) -> String {
    format!("arg{idx}")
}
